﻿module FunctionalAzure.Common

open System
open System.Net
open System.Security.Cryptography
open System.Web

let canonicalize_headers (headers: WebHeaderCollection) = 
    String.concat "" (
        Array.map (fun (x : string) -> String.Format("{0}:{1}\n", x.ToLower(), headers.Get(x))) (
            Array.sort (
                Array.filter (fun (x : string) -> x.StartsWith("x-ms-")) headers.AllKeys
            )
        )
    )

let canonicalize_resource (user: string) (resource : string)=
    let delimiter_index = resource.IndexOf("?")
    if delimiter_index = -1 then
        String.Format("/{0}{1}", user, resource)
    else
        let splitters : char[] = "&".ToCharArray()
        let path = resource.Substring(0, delimiter_index)
        let data = Array.toList (resource.Substring(delimiter_index + 1).Split(splitters))
        let data_without_filters = List.filter (fun (x : string)-> not ((x.Chars 0) = '$')) data
        let get_key_val (x : string) = 
            let delimiter_index = x.IndexOf("=")
            (x.Substring(0, delimiter_index), x.Substring(delimiter_index + 1))
        let rec sort a = 
            if List.isEmpty a then
                a
            else
                let first (x, _) = x
                let element = a.Head
                let element_key = first(get_key_val element)
                let less, more_or_equal = List.partition (fun x -> first(get_key_val x) < element_key) a.Tail
                let more = List.filter (fun x -> not (x = element_key)) more_or_equal
                (sort less)@[element]@(sort more)
                    
        let sorted_params = sort data_without_filters
        if List.isEmpty data_without_filters then
            String.Format("/{0}{1}", user, path)
        else
            let decode_param p = 
                let key, value = get_key_val p
                HttpUtility.UrlDecode(key.ToLower()) + ":" + HttpUtility.UrlDecode(value)
            String.Format("/{0}{1}\n{2}", user, path, String.Join("\n", List.map decode_param sorted_params))            

let get_authorization (user: string) (key: string) (canonicalizedString : string) =
    let hasher = new HMACSHA256(System.Convert.FromBase64String(key))
    let data = System.Text.Encoding.UTF8.GetBytes(canonicalizedString)
    let signature = System.Convert.ToBase64String(hasher.ComputeHash(data))
    System.String.Format(System.Globalization.CultureInfo.InvariantCulture, "SharedKey {0}:{1}", user, signature)